package bot.module.th.estimation;

import game.gamestorage.texas.db.estimation.GetterDBProjector;
import game.model.Card;

import java.util.HashMap;

import util.DebugOut;
import util.sql.DBHelper;
import bot.module.equity.OutsAndOdds;

/**
 * This class estimates the opponent at Turn</br>
 * - NOT IMPLEMENTED YET
 * 
 * @author Witthold/Korol
 */
public class Turn implements IF_EstimationAtRound {

	/**
	 * shc comparison - consider: tightness only via folds/totalBets<br>
	 * int[0]: relation looseness, 0-100<br>
	 * int[1]: relation aggression, 0-100<br>
	 * int[2]: numMoreLoose<br>
	 * int[3]: numMoreAgressive<br>
	 * int[4]: numAllBets<br>
	 * int[5]: numPlayedBets
	 */
	protected int[] behaviour = new int[6];
	/** 
	 * amount of
	 * int[0]: folds<br>
	 * int[1]: checks, calls<br>
	 * int[2]: raises<br>
	 * int[3]: allIns<br>
	 */
	private int[] actions = new int[4];
//	private SubjectiveAllinEquity saie;
	private OutsAndOdds oo;
	private GetterDBProjector gDBP;

	public Turn() {
//		saie = new SubjectiveAllinEquity(GameTypeInt.GAME_HOLDEM, 5); // TODO read these arguments of the gamedef
		oo = new OutsAndOdds();
	}

	/* (non-Javadoc)
	 * @see bot.module.th.estimation.IF_EstimationAtRound#fetchData(game.gamestorage.texas.db.estimation.GetterDBProjector, int, int, int)
	 */
	@Override
	public void fetchData(GetterDBProjector gDBP) {
		this.gDBP = gDBP;
		gDBP.setTurnData();
		behaviour[0] = 50;
		behaviour[1] = 50;
	}


	/* (non-Javadoc)
	 * @see bot.module.th.estimation.IF_EstimationAtRound#estimate()
	 */
	@Override
	public void estimate() {
		for(Integer key : gDBP.getHpmsTurnBetsCO().keySet()) {
			/* board cards */
			Card[] flopCards = DBHelper.cardsDBToCardArr(gDBP.getFlopCards().get(key));
			Card[] turnCards = DBHelper.cardsDBToCardArr(gDBP.getTurnCards().get(key));
			Card[] boardCards = new Card[flopCards.length+turnCards.length];
			for(int i=0; i<boardCards.length; i++) {
				if(i<flopCards.length)
					boardCards[i ] = flopCards[i];
				else
					boardCards[i] = turnCards[i-flopCards.length];
			}
			
			/* hole cards */
			HashMap<Integer, Card[]> holeCards = new HashMap<Integer, Card[]>();
			holeCards.put(gDBP.getPlayerId(), DBHelper.cardsDBToCardArr(gDBP.getHoleCards().get(key)));
			int hid = gDBP.getHandPlayerMapCO().get(key)[3];
			HashMap<Integer, int[]> cardsDB = gDBP.getAllHoleCards(hid);
			for(Integer player : cardsDB.keySet()) {
				holeCards.put(player, DBHelper.cardsDBToCardArr(cardsDB.get(player)));
			}
			Card[][] holeCards_in = new Card[holeCards.size()][2];
			int i = 0;
			int[] i_map = new int[holeCards.size()];
			double[] odds = new double[holeCards.size()];
			for(Integer player : holeCards.keySet()) {
				holeCards_in[i] = holeCards.get(player);
				i_map[i] = player;
				odds[i] = oo.getOdds(2, holeCards_in[i], boardCards);
				i++;
			}
			
			/* equity proposal */
//			 double[] equities = saie.getEquity(1000, 1000, holeCards_in, boardCards, null);
			// TODO equities don't work
			int player_i = 0;
			for(i=0; i<i_map.length; i++) {
				if(i_map[i]==gDBP.getPlayerId())
					player_i = i;
			}
			long potsize = gDBP.getHpmsTurnBetsCO().get(key)[3];
			long callsize = gDBP.getCallSizeAtTurnCO(key); //TODO callsize incorrect
			long action = gDBP.getHpmsTurnBetsCO().get(key)[1];
			double potodds =  (((double) callsize) / ((double) potsize+ (double) callsize)) * 100;
			int intAction = (int) action;
			switch(intAction) {
			case -1: actions[0]++; break;
			case 0: actions[1]++; break;
			case 1: actions[1]++; break;
			case 5: actions[2]++; break;
			case 9: actions[3]++; break;
			}
			if (odds[player_i] >= 4*potodds) {
				/* raise proposed */
				if(action < 0 ) // player folded
					behaviour[2]-=2; // very tight
				else if(action <= 1) // player called
					behaviour[3]--; // passive
			} else if (odds[player_i] >= potodds) {
				/* call proposed */
				if(action < 0 ) // player folded
					behaviour[2]--; // tight
				else if(action>=5) // player raised
					behaviour[3]++; // aggressive
			} else {
				/* fold proposed */
				if(action >= 0 && action <=1) // player called
					behaviour[2]++; // loose
				else if(action>=5) { // player raised
					behaviour[2]+=2; // very loose
					behaviour[3]++; // aggressive
				}
					
			}
			behaviour[4]++; // round is counted
			if(action>0) behaviour[5]++; // round is counted as played
			
		}
	}


	/* (non-Javadoc)
	 * @see bot.module.th.estimation.IF_EstimationAtRound#mergeIntoRound()
	 */
	@Override
	public void mergeIntoRound() {
		double ratio = new Double(behaviour[2]) / new Double(behaviour[4]);
		double value = new Double(100-behaviour[0]) * ratio;
		behaviour[0] = behaviour[0] + (int) value;
		behaviour[0] = (behaviour[0]<0) ? 0 : (behaviour[0]>100 ? 100 : behaviour[0]);
		
		ratio = new Double(behaviour[3]) / new Double(behaviour[5]);
		value = new Double(100-behaviour[1]) * ratio;
		behaviour[1] = behaviour[1] + (int) value;
		behaviour[1] = (behaviour[1]<0) ? 0 : (behaviour[1]>100 ? 100 : behaviour[1]);
	}


	/* (non-Javadoc)
	 * @see bot.module.th.estimation.IF_EstimationAtRound#mergeIntoOverall()
	 */
	@Override
	public void mergeIntoOverall(Opponent o) {
		for (int i = 0; i < actions.length; i++) {
			DebugOut.showVerboseEstimation("actions" + i + ": " + actions[i]);
		}
		
		int[] cat = {behaviour[0], behaviour[1], 0};
		o.setBehaviourAtRound(2, cat);
		
		o.setActionAtRound(2, actions);
	}

}
